package cn.czu.wyw.controller;

import cn.czu.wyw.annocation.LogAnnotation;
import cn.czu.wyw.pojo.Users;
import cn.czu.wyw.service.IUserService;
import cn.czu.wyw.utils.CommonJsonResult;
import cn.czu.wyw.utils.MD5Utils;
import cn.czu.wyw.utils.RedisUtil;
import cn.czu.wyw.vo.UserVo;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.UUID;

/**
 * @Author wyw
 * @Description
 * @Date 2022/1/14 16:19
 * @Version 1.0
 */
@Api("自定义APi")
@Slf4j
@Controller
@RequestMapping("/sso")
public class SSOController {

    @Autowired
    RedisUtil redisUtil;

    private IUserService userService;
    @Autowired
    public void setUserService(IUserService userService) {
        this.userService = userService;
    }

    private static final String REDIS_USER_TOKEN = "redis_user_token:";
    private static final String REDIS_USER_TICKET = "redis_user_ticket:";
    private static final String REDIS_TMP_TICKET = "redis_tmp_ticket:";

    private static final String COOKIE_USER_TICKET = "cookie_user_ticket";

    @LogAnnotation
    @ApiOperation("test")
    @GetMapping("/test")
    public void test( HttpServletResponse response,
                      HttpServletRequest request){
        getDomainName(request);
    }

    @LogAnnotation
    @ApiOperation("login")
    @GetMapping("/login")
    public String login(
            @ApiParam(name = "returnUrl",value = "返回url", required = true) @RequestParam("returnUrl") String returnUrl,
            Model model ,
            HttpServletResponse response,
            HttpServletRequest request
    ){
        model.addAttribute("returnUrl",returnUrl);

        // 1、获取usetTicket门票，如果cookie能够获取，分发一次性临时门票
        String userTicket = getCookie(COOKIE_USER_TICKET,request);
        boolean  isVerified = verifyTemTicket(userTicket);

        if(isVerified){
            String tmpTicket = createTmpTicket();
            return "redirect:"+returnUrl+"?tmpTicket="+tmpTicket;
        }
        // 用户从未登陆过，则跳转到CAS统一登陆页面
        return "login";
    }

    @ApiOperation("cas 退出登录")
    @PostMapping("/logout")
    @LogAnnotation
    @ResponseBody
    private CommonJsonResult logout(
            String userId,
            HttpServletResponse response,
            HttpServletRequest request){

        // 1、获取用户门票
        String userTicket = getCookie(COOKIE_USER_TICKET,request);

        // 2、清除userTicket信息   redis/cookie
        delCookie(COOKIE_USER_TICKET,response, request);
        redisUtil.del(REDIS_USER_TICKET.concat(userTicket));

        // 清除全局会话
        redisUtil.del(REDIS_USER_TOKEN.concat(userId));

        return CommonJsonResult.ok();
    }
    /**
     * cas 统一登录接口
     *   目的：
     *      1、登陆后，创建全局会话   -》 uniqueToken
     *      2、创建用户全局门票,表示CAS端是否登录  -》 userTicket
     *      3、创建用户临时票据，回调回传  -》  tmpTicket
     *  dodoLogin 需要和 thymeleaf action相等
     * @return 重定向页面
     */
    @ApiOperation("cas 统一登录接口")
    @PostMapping("/doLogin")
    @LogAnnotation
    public String doLogin(
            String username,
            String password,
            String returnUrl,
            Model model ,
            HttpServletResponse response,
            HttpServletRequest request
    ){
        // 1、实现登录
        if (StringUtils.isBlank(username) || StringUtils.isBlank(password)) {
            model.addAttribute("errmsg","用户名或密码不能为空");
            return "login";
        }
        Users userResult = userService.queryUserForLogin(username, password);
        if (userResult == null) {
            model.addAttribute("errmsg","用户名或密码不正确");
            return "login";
        }

        //2、实现用户的redis会话
        String uniqueToken = UUID.randomUUID().toString().trim();
        UserVo userVo = new UserVo();
        BeanUtils.copyProperties(userResult, userVo);
        userVo.setUserUniqueToken(uniqueToken);
        redisUtil.set(REDIS_USER_TOKEN.concat(userVo.getId()), JSON.toJSONString(userVo), 60 * 60 * 24);

        // 3、生产全局门票,关联用户ID ,放入redis
        String  userTicket = UUID.randomUUID().toString().trim();
        redisUtil.set(REDIS_USER_TICKET.concat(userTicket), userVo.getId(), 60 * 60 * 24);
        // 3.1  全局门票放入cookie中
        setCookie(COOKIE_USER_TICKET,userTicket,response,request);

        // 4、 生产临时票据，由CAS端签发的一个一次性临时ticket
        String tmpTicket = createTmpTicket();
        //return "login";
       return "redirect:"+returnUrl+"?tmpTicket="+tmpTicket;
    }
    @ApiOperation("cas 校验临时票据")
    @PostMapping("/verifyTemTicket/{tmpTicket}")
    @LogAnnotation
    @ResponseBody
    private CommonJsonResult verifyTemTicket(@PathVariable("tmpTicket") String tmpTicket,
                                             HttpServletResponse response,
                                             HttpServletRequest request) throws Exception {
        // 验证临时票据，如果登陆过，把  会话信息返回给站点
        // 使用完毕后，需要销毁临时票据
        String tmpTicketValue = redisUtil.get(REDIS_TMP_TICKET.concat(tmpTicket));
        if (tmpTicketValue == null){
            return CommonJsonResult.errorUserTicket("用户门票异常");
        }
        // 1、临时票据正常，则需要销毁，并获取CAS中的全局ticket，以便获取会话
        if(!tmpTicketValue.equals(MD5Utils.getMD5Str(tmpTicket))){
            return CommonJsonResult.errorUserTicket("用户门票异常");
        }else {
            redisUtil.del(REDIS_TMP_TICKET.concat(tmpTicket));
        }
        // 获取用户的userTicket
        String userTicket = getCookie(COOKIE_USER_TICKET,request);
        String userId = redisUtil.get(REDIS_USER_TICKET.concat(userTicket));
        if(StringUtils.isBlank(userId)){
            return CommonJsonResult.errorUserTicket("用户门票异常");
        }
        // 获取用户信息
        String userRedis = redisUtil.get(REDIS_USER_TOKEN.concat(userId));
        if(StringUtils.isBlank(userId)){
            return CommonJsonResult.errorUserTicket("用户门票异常");
        }
        return CommonJsonResult.ok(JSONObject.parseObject(userRedis,UserVo.class));
    }
    /**
     *  创建全局用户门票
     * @return void
     */
    private String createTmpTicket(){
        String  tmpTicket = UUID.randomUUID().toString().trim();
        try {
            redisUtil.set(REDIS_TMP_TICKET.concat(tmpTicket), MD5Utils.getMD5Str(tmpTicket), 60 );
        } catch (Exception e) {
            e.printStackTrace();
        }
        return tmpTicket;
    }
    private void setCookie(String key, String value, HttpServletResponse response, HttpServletRequest request){
        Cookie cookie = new Cookie(key, value);
        // 设置跨域共享
        cookie.setDomain(getDomainName(request));

        // 设置同服务器共享
        cookie.setPath("/");

        response.addCookie(cookie);
    }

    /**
     *  获取cookie的值
     * @param key
     * @param request
     * @return
     */
    private String getCookie(String key,  HttpServletRequest request){
        Cookie[] cookies = request.getCookies();
        if (cookies == null || StringUtils.isBlank(key)){
            return  "";
        }
        String cookieValue = "";
        for(Cookie cookie:cookies){
            if (cookie.getName().equals(key)){
                cookieValue = cookie.getName();
            }
        }
        return cookieValue;
    }


    private void delCookie(String key,  HttpServletResponse response,HttpServletRequest request){
        Cookie cookie = new Cookie(key, null);
        // 设置跨域共享
        cookie.setDomain(getDomainName(request));

        // 设置同服务器共享
        cookie.setPath("/");

        cookie.setMaxAge(-1);
        response.addCookie(cookie);
    }


    /**
     * 获取域名
     */
    private static String getDomainName(HttpServletRequest request) {
        String domainName;
        String serverName = request.getRequestURL().toString();
        if ("".equals(serverName)) {
            domainName = "";
        } else {
            serverName = serverName.toLowerCase();
            serverName = serverName.substring(7);
            final int end = serverName.indexOf("/");
            serverName = serverName.substring(0, end);
            if (serverName.indexOf(":") > 0) {
                String[] ary = serverName.split("\\:");
                serverName = ary[0];
            }

            final String[] domains = serverName.split("\\.");
            int len = domains.length;
            if (len > 3 && !isIp(serverName)) {
                // www.xxx.com.cn
                domainName = "." + domains[len - 3] + "." + domains[len - 2] + "." + domains[len - 1];
            } else if (len <= 3 && len > 1) {
                // xxx.com or xxx.cn
                domainName = "." + domains[len - 2] + "." + domains[len - 1];
            } else {
                domainName = serverName;
            }
        }
        return domainName;
    }

    /**
     *  判断是否为 Ip
     * @param IP
     * @return
     */
    public static boolean isIp(String IP){
        boolean b = false;
        IP = trimSpaces(IP);
        if(IP.matches("^\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}$")){
            String s[] = IP.split("\\.");
            if(Integer.parseInt(s[0])<255) {
                if(Integer.parseInt(s[1])<255) {
                    if(Integer.parseInt(s[2])<255) {
                        if(Integer.parseInt(s[3])<255) {
                            b = true;
                        }
                    }
                }
            }
        }
        return b;
    }

    /**
     * 去掉IP字符串前后所有的空格
     * @param IP
     * @return 去除空格的ip
     */
    public static String trimSpaces(String IP){
        while(IP.startsWith(" ")){
            IP= IP.substring(1,IP.length()).trim();
        }
        while(IP.endsWith(" ")){
            IP= IP.substring(0,IP.length()-1).trim();
        }
        return IP;
    }

    /**
     *  校验cas全局用户门票
     * @param userTicket
     * @return
     */
    public boolean verifyTemTicket(String userTicket){
        // 验证门票不能为空
        if (StringUtils.isBlank(userTicket)){
            return false;
        }

        // 验证cas 门票是否有效
        String userId = redisUtil.get(REDIS_USER_TICKET.concat(userTicket));
        if(StringUtils.isBlank(userId)){
            return false;
        }

        // 验证门票对于user是否存在
        String userRedis = redisUtil.get(REDIS_USER_TOKEN.concat(userId));
        if(StringUtils.isBlank(userRedis)){
            return false;
        }
        return true;
    }
}
